#ifndef SEM_V1_HPP
#define SEM_V1_HPP

#include<iostream>
#include <memory>
#include<string>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<unistd.h>

//简单的建造者模式

const std::string PATH_NAME = "/tmp";
const int PROJ_ID = 0x77;
const int defaultnsem = 1;
const int Sem_P = -1;
const int Sem_V = 1;
#define GET_SEM (IPC_CREAT)
#define BUILD_SEM (IPC_CREAT | IPC_EXCL | 0666)


//仅关注使用和删除
class Semaphore
{
private:
    void PV(int PorV)
    {
        struct sembuf sb;
        sb.sem_num = 0;          //信号量编号，从0开始
        sb.sem_op = PorV;        //S + sem_buf.sem_op
        sb.sem_flg = SEM_UNDO;   //不关心
        int n = ::semop(_semid, &sb, 1);
        (void)n;
    }
public:
    Semaphore(int semid):_semid(semid)
    {}

    ~Semaphore()
    {
        if(_semid >= 0)
        {
            int n = ::semctl(_semid,0,IPC_RMID);
            if(n < 0)
            {
                std::cerr << "semctl IPC_RMID Error" << std::endl;
            }
            std::cout << "Semphore " << _semid << " removed" << std::endl;
        }
    }

    void P() {PV(Sem_P);}

    void V() {PV(Sem_V);}
private:
    int _semid;
};

//关注建造
class SemaphoreBuilder
{
public:
    std::string IntoHex(key_t key)
    {
        char buf[64];
        snprintf(buf,sizeof(buf),"0x%x",key);
        return std::string(buf);
    }

    SemaphoreBuilder()
    {}

    ~SemaphoreBuilder()
    {}

    SemaphoreBuilder &SetVal(int val)
    {
        _val = val;
        return *this;
    } 

    

    std::shared_ptr<Semaphore> Build(int flag,const int nsem = defaultnsem)
    {
        std::cout << "Build Semaphore" << std::endl;
        //1.创建键值
        key_t key = ::ftok(PATH_NAME.c_str(),PROJ_ID);
        if(key < 0)
        {
            std::cerr << "Got Key Error" << std::endl;
            return nullptr;
        }
        std::cout << "Got Key" << IntoHex(key) << std::endl;
        //2.创建信号集
        int semid = ::semget(key,nsem,flag);
        if(semid < 0)
        {
            std::cerr << "Create Sem Error" << std::endl;
            return nullptr;
        }
        //3.初始化
        if(flag == BUILD_SEM)
            Init(semid,0);

        return std::make_shared<Semaphore>(semid);
    }
private:
    void Init(int semid,int num)
    {
        // 3. 初始化信号量
        union semun // 该联合体系统不提供，需要我们自己定义
        {
            int val;               /* Value for SETVAL */
            struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
            unsigned short *array; /* Array for GETALL, SETALL */
            struct seminfo *__buf; /* Buffer for IPC_INFO
                                        (Linux-specific) */
        } un;

        un.val = _val; // 设置为初始值

        int n = ::semctl(semid, num, SETVAL, un);
        if (n < 0)
        {
            std::cerr << "Init Error" << std::endl;
            return;
        }
    }
private:
    int _val;  //所有信号量的初始值
};

#endif